package com.example.demokotlin

import org.junit.Test
import kotlin.random.Random


/**
 * 标准库函数
 * 又称为作用域函数：let，run，with，apply，和also。
 *
 * @author zhouronghua
 * @time 2021/9/12 8:42 上午
 *
 *  函数	    对象引用	返回值	                        是否是扩展函数
 *  let	        it	            Lambda 表达式结果	是
 *  run	        this	        Lambda 表达式结果	是
 *  run	        -	            Lambda 表达式结果	不是：调用无需上下文对象
 *  with	    this	        Lambda 表达式结果	不是：把上下文对象当做参数
 *  apply	    this	        上下文对象	                是
 *  also	    it	            上下文对象	                是
 */
class StandardTest {

    /**
     * 测试内置函数let
     * 说明: let的返回值为lambda表达式的返回值
     * T.let函数与T.run函数唯一的区别是：T作为作用域上下文对象的名称不同，前者是it，后者是this，
     * 所以在T.let函数中必须显式使用it访问T对象。
     *
     * @author zhouronghua
     * @time 2021/9/12 8:48 上午
     */
    @Test
    fun testLet() {
        /**
         * let将it进行变化，函数体内最后一行作为返回值
         */
        val result = listOf(4, 3, 2, 1).first().let {
            // 返回值是4*4=16
            it * it
        }
        println("first item is $result")
        // 测试祝福语
        println(greetNotNull("Lily"))
        // 传入空字符串
        println(greetNotNull(null))
        // 测试修改变量值
        var person = Person("小李", 18)
        println("person name ${person.name}, age: ${person.age}")
        person.let {
            it.age = 30
        }
        println("person name ${person.name}, age: ${person.age}")
    }

    /**
     * 祝福语
     *
     * @author zhouronghua
     * @time 2021/9/12 8:57 上午
     */
    fun greetNotNull(str: String?): String {
        return str?.let {
            "Good Morning $it"
        } ?: "Nice To meet you"
    }

    /**
     * run函数
     *   1) run函数创建了一个单独的作用域，在该作用域中重新定义了一个word变量，
     *   两次打印使用的是各自作用域中的word变量，互不影响；并且，run函数返回了lambda结果。
     *   2）run内部持有对象的引用，
     *
     * @author zhouronghua
     * @time 2021/9/14 5:48 下午
     */
    @Test
    fun testRun() {
        println("==== 测试run函数 ====")
        var word = "我是小明"
        val returnValue = run {
            var word = "我是小红"
            println("run:$word")
            // 隐私函数中，lambda表达是最后一行为返回的值。
            // run中最后一行是返回值，此处word的作用域为lambda表达式内部的作用域，为"我是小红"
            word
        }
        println("main:$word")
        println("returnValue:$returnValue")
        // run持有对象的引用
        var desc: String = "i like 中国人"
        var desc2 = desc.run {
            // 引用对象的首字母转换为大写并且将结果返回
            desc.capitalize()
        }
        println("$desc after run is to $desc2")
    }

    /**
     * With表达式
     * 说明: with函数可以将任意对象作为上下文对象this传入，
     * 并且可以隐式的访问该对象，返回lambda结果。
     *
     * @author zhouronghua
     * @time 2021/9/14 5:42 下午
     */
    @Test
    fun testWith() {
        println("==== 测试With ====")
        var person = Person("小明", 25)
        val returnValue = with(person) {
            // 此处是传入对象Person("小明",25)
            println("with:this=$this")
            // 临时变量person
            var person = Person("小红", 23)
            println("with:person=$person")
            // 传入的上下文person对象的age设置为26
            age = 26
            // 返回的是lambda表达式中临时变量person
            person
        }
        println("main:person=$person")
        println("main:returnValue=$returnValue")
    }

    /**
     * 测试also
     * T.also函数和T.let函数的唯一区别是：前者返回值是this（即T），后者返回值是lambda结果。
     *
     * @author zhouronghua
     * @time 2021/9/14 6:07 下午
     */
    @Test
    fun testAlso() {
        println("==== 测试also ====")
        var person: Person? = null
        person?.also {
            println("person?.also:person=$person")
        }
        person = Person("小明", 25)
        val returnValue = person.also {
            // 传入的person对象Person("小明",25)
            println("person.also:it=$it")
            // 创建临时变量
            var person = Person("小红", 23)
            // 打印的是临时变量的值
            println("person.also:person=$person")
            // 传入对象的age设置为26
            it.age = 26
            // 此处不影响返回值，also的返回值是传入参数对象
            person
        }
        println("main:person=$person")
        println("main:returnValue=$returnValue")
    }

    /**
     * apply上下文对象 作为接收者（this）来访问。 返回值 是上下文对象本身。
     *
     * @author zhouronghua
     * @time 2021/9/20 10:53 上午
     */
    @Test
    fun testApply() {
        println("==== 测试apply ====")
        val person = Person("小明", 25)
            .apply {
                // 传入的是this指针，直接修改this的成员值，然后返回
                this.age = 30
            }
        println(person)
    }

    /**
     * 显示子串的位置
     *
     * @author zhouronghua
     * @time 2021/9/20 11:32 上午
     */
    fun displaySubstringPosition(input: String, sub: String) {
        input.indexOf(sub).takeIf { it >= 0 }?.let {
            println("The substring $sub is found in $input.")
            println("Its start position is $it.")
        }
    }

    /**
     * 测试标准函数takeIf
     *
     * @author zhouronghua
     * @time 2021/9/20 11:08 上午
     */
    @Test
    fun testTakeIf() {
        println("==== 测试takeIf：满足条件则返回原值 ====")
        val random = Random.nextInt(100)
        // takeIf以提供的谓词在对象上进行调用时，若该对象与谓词匹配，则 takeIf 返回此对象。
        val ifOrNull = random.takeIf {
            it % 2 == 0
        }
        // takeUnless如果不匹配谓词，则返回对象，如果匹配则返回 null
        val unlessOrNull = random.takeUnless {
            it % 2 == 0
        }
        println("random is $random, test takeIf $ifOrNull, takeUnless $unlessOrNull")
        // 如果字符串内容不为空则转换为大写
        val str = "Hello Welcome to kotlin world"
        val cap = str.takeIf {
            // 如果不为空则返回原值，否则返回null
            it.isNotEmpty()
        }?.toUpperCase()
        println(cap)
        // 查找子串位置
        displaySubstringPosition("010000011", "11")
        displaySubstringPosition("010000011", "12")
    }
}